/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
Copyright (C) 2006 Robert Beckebans <trebor_7@users.sourceforge.net>

This file is part of XreaL source code.

XreaL source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.

XreaL source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with XreaL source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
===========================================================================
*/
#include "qbsp.h"


void PrintCtrl(vec3_t ctrl[9])
{
	int             i, j;

	for(i = 0; i < 3; i++)
	{
		for(j = 0; j < 3; j++)
		{
			Sys_Printf("(%5.2f %5.2f %5.2f) ", ctrl[i * 3 + j][0], ctrl[i * 3 + j][1], ctrl[i * 3 + j][2]);
		}
		Sys_Printf("\n");
	}
}

/*
================
DrawSurfaceForMesh
================
*/
drawSurface_t  *DrawSurfaceForMesh(mesh_t * m)
{
	drawSurface_t  *ds;
	int             i, j;
	mesh_t         *copy;

	// to make valid normals for patches with degenerate edges,
	// we need to make a copy of the mesh and put the aproximating
	// points onto the curve
	copy = CopyMesh(m);
	PutMeshOnCurve(*copy);
	MakeMeshNormals(*copy);
	for(j = 0; j < m->width; j++)
	{
		for(i = 0; i < m->height; i++)
		{
			VectorCopy(copy->verts[i * m->width + j].normal, m->verts[i * m->width + j].normal);
		}
	}
	FreeMesh(copy);

	ds = AllocDrawSurf();
	ds->mapBrush = NULL;
	ds->side = NULL;

	ds->patch = qtrue;
	ds->patchWidth = m->width;
	ds->patchHeight = m->height;
	ds->numVerts = ds->patchWidth * ds->patchHeight;
	ds->verts = malloc(ds->numVerts * sizeof(*ds->verts));
	memcpy(ds->verts, m->verts, ds->numVerts * sizeof(*ds->verts));

	ds->lightmapNum = -1;
	ds->fogNum = -1;

	return ds;
}

/*
=================
ParsePatch

Creates a drawSurface_t from the patch text
=================
*/
void ParsePatch(qboolean patchDef3)
{
	vec_t           info[7];
	int             i, j;
	parseMesh_t    *pm;
	char            name[MAX_QPATH];
	char            shader[MAX_QPATH];
	mesh_t          m;
	drawVert_t     *verts;
	epair_t        *ep;

	MatchToken("{");

	// get texture
	GetToken(qtrue);
	strcpy(name, token);

	// save the shader name for retexturing
	if(numMapIndexedShaders == MAX_MAP_BRUSHSIDES)
	{
		Error("MAX_MAP_BRUSHSIDES");
	}
	strcpy(mapIndexedShaders[numMapIndexedShaders], name);
	numMapIndexedShaders++;

	if(patchDef3)
		Parse1DMatrix(7, info);
	else
		Parse1DMatrix(5, info);

	m.width = info[0];
	m.height = info[1];
	m.verts = verts = malloc(m.width * m.height * sizeof(m.verts[0]));

	if(m.width < 0 || m.width > MAX_PATCH_SIZE || m.height < 0 || m.height > MAX_PATCH_SIZE)
	{
		Error("ParsePatch: bad size");
	}

	MatchToken("(");
	for(j = 0; j < m.width; j++)
	{
		MatchToken("(");
		for(i = 0; i < m.height; i++)
		{
			Parse1DMatrix(5, verts[i * m.width + j].xyz);
		}
		MatchToken(")");
	}
	MatchToken(")");

	// if brush primitives format, we may have some epairs to ignore here
	GetToken(qtrue);
	if(g_bBrushPrimit != BPRIMIT_OLDBRUSHES && strcmp(token, "}"))
	{
		// NOTE: we leak that!
		ep = ParseEpair();
	}
	else
		UnGetToken();

	MatchToken("}");
	MatchToken("}");

	if(nocurves)
	{
		return;
	}

	// find default flags and values
	pm = malloc(sizeof(*pm));
	memset(pm, 0, sizeof(*pm));

	pm->patchDef3 = patchDef3;

	if(patchDef3)
	{
		for(j = 0; j < 7; j++)
		{
			pm->info[j] = info[j];
		}
	}
	else
	{
		for(j = 0; j < 5; j++)
		{
			pm->info[j] = info[j];
		}
	}


	if(!Q_strncasecmp(name, "textures/", 9))
		sprintf(shader, "%s", name);
	else
		sprintf(shader, "textures/%s", name);

	pm->shaderInfo = ShaderInfoForShader(shader);
	pm->mesh = m;

	// link to the entity
	pm->next = mapEnt->patches;
	mapEnt->patches = pm;
}


void GrowGroup_r(int patchNum, int patchCount, const byte * bordering, byte * group)
{
	int             i;
	const byte     *row;

	if(group[patchNum])
	{
		return;
	}
	group[patchNum] = 1;
	row = bordering + patchNum * patchCount;
	for(i = 0; i < patchCount; i++)
	{
		if(row[i])
		{
			GrowGroup_r(i, patchCount, bordering, group);
		}
	}
}


/*
=====================
PatchMapDrawSurfs

Any patches that share an edge need to choose their
level of detail as a unit, otherwise the edges would
pull apart.
=====================
*/
void PatchMapDrawSurfs(entity_t * e)
{
	parseMesh_t    *pm;
	parseMesh_t    *check, *scan;
	drawSurface_t  *ds;
	int             patchCount, groupCount;
	int             i, j, k, l, c1, c2;
	drawVert_t     *v1, *v2;
	vec3_t          bounds[2];
	byte           *bordering;
	parseMesh_t    *meshes[MAX_MAP_DRAW_SURFS];
	qboolean        grouped[MAX_MAP_DRAW_SURFS];
	byte            group[MAX_MAP_DRAW_SURFS];

	Sys_FPrintf(SYS_VRB, "----- PatchMapDrawSurfs -----\n");

	patchCount = 0;
	for(pm = e->patches; pm; pm = pm->next)
	{
		meshes[patchCount] = pm;
		patchCount++;
	}

	if(!patchCount)
	{
		return;
	}
	bordering = malloc(patchCount * patchCount);
	memset(bordering, 0, patchCount * patchCount);

	// build the bordering matrix
	for(k = 0; k < patchCount; k++)
	{
		bordering[k * patchCount + k] = 1;

		for(l = k + 1; l < patchCount; l++)
		{
			check = meshes[k];
			scan = meshes[l];
			c1 = scan->mesh.width * scan->mesh.height;
			v1 = scan->mesh.verts;

			for(i = 0; i < c1; i++, v1++)
			{
				c2 = check->mesh.width * check->mesh.height;
				v2 = check->mesh.verts;
				for(j = 0; j < c2; j++, v2++)
				{
					if(fabs(v1->xyz[0] - v2->xyz[0]) < 1.0
					   && fabs(v1->xyz[1] - v2->xyz[1]) < 1.0 && fabs(v1->xyz[2] - v2->xyz[2]) < 1.0)
					{
						break;
					}
				}
				if(j != c2)
				{
					break;
				}
			}
			if(i != c1)
			{
				// we have a connection
				bordering[k * patchCount + l] = bordering[l * patchCount + k] = 1;
			}
			else
			{
				// no connection
				bordering[k * patchCount + l] = bordering[l * patchCount + k] = 0;
			}

		}
	}

	// build groups
	memset(grouped, 0, sizeof(grouped));
	groupCount = 0;
	for(i = 0; i < patchCount; i++)
	{
		if(!grouped[i])
		{
			groupCount++;
		}

		// recursively find all patches that belong in the same group
		memset(group, 0, patchCount);
		GrowGroup_r(i, patchCount, bordering, group);

		// bound them
		ClearBounds(bounds[0], bounds[1]);
		for(j = 0; j < patchCount; j++)
		{
			if(group[j])
			{
				grouped[j] = qtrue;
				scan = meshes[j];
				c1 = scan->mesh.width * scan->mesh.height;
				v1 = scan->mesh.verts;
				for(k = 0; k < c1; k++, v1++)
				{
					AddPointToBounds(v1->xyz, bounds[0], bounds[1]);
				}
			}
		}

		// create drawsurf
		scan = meshes[i];
		scan->grouped = qtrue;
		ds = DrawSurfaceForMesh(&scan->mesh);
		ds->shaderInfo = scan->shaderInfo;
		VectorCopy(bounds[0], ds->lightmapVecs[0]);
		VectorCopy(bounds[1], ds->lightmapVecs[1]);
	}

	Sys_FPrintf(SYS_VRB, "%5i patches\n", patchCount);
	Sys_FPrintf(SYS_VRB, "%5i patch LOD groups\n", groupCount);
}
